﻿
#pragma once

#include "tools/value.h"


#define ONLY_FOR_INTEGER \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_INT, T >::type
#define ONLY_FOR_FLOATING \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_FLOAT, T >::type
#define ONLY_FOR_STRICT \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_STRICT, T >::type
#define ONLY_FOR_MESSAGE \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_MESSAGE, T >::type
#define COMPILE_ERROR_FOR_OTHER \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_OTHER, T >::type

namespace tools{

struct Message: value_detail::ValueTypes
{
   ~Message(){}
    Message(const Str& name="",const size_t reserve=4)
        :mName(name)
    {
        mRecords.reserve(reserve);
    }

    void SetName(const Str& name){ mName = name; }
    const Str& GetName()const { return mName;    }

    Message& operator()(const Str& name, const char* v){ mRecords.emplace_back(name, Str(v)); return *this; }
    Message& operator()(const char* v){ return operator()("", v); }
    Message& operator()(const Str& name, const Message& v){ mRecords.emplace_back( name, v.mRecords ); return *this; }
    Message& operator()(const Message& v){ return operator()("", v); }

    template<class T>Message& operator()(const Str& name, const T& v){ mRecords.emplace_back( name, Value::MakeMinsize(v) ); return *this; }
    template<class T>Message& operator()(const T& v){ return operator()("", v); }

    Message& Add(const Str& name, const char* v)               { return operator()(name, v); }
    Message& Add(const char* v)                                { return operator()(v);       }
    Message& Add(const Str& name, const Message& v)            { return operator()(name, v); }
    Message& Add(const Message& v)                             { return operator()(v);       }
    template<class T>Message& Add(const Str& name, const T& v) { return operator()(name, v); }
    template<class T>Message& Add(const T& v)                  { return operator()(v);       }
public:
    template<class T> ONLY_FOR_INTEGER        As(const Str& name)const { return operator[](name).As<T>(); }
    template<class T> ONLY_FOR_FLOATING       As(const Str& name)const { return operator[](name).As<T>(); }
    template<class T> const ONLY_FOR_STRICT&  As(const Str& name)const { return operator[](name).As<T>(); }
    template<class T> ONLY_FOR_STRICT&        As(const Str& name)      { return operator[](name).As<T>(); }
    template<class T> COMPILE_ERROR_FOR_OTHER As(const Str& name)const { static_assert( false, "THIS_TYPE_IS_NOT_SUPPORT" ); }

    template<class T> ONLY_FOR_INTEGER        As(const size_t& index)const { return operator[](index).As<T>(); }
    template<class T> ONLY_FOR_FLOATING       As(const size_t& index)const { return operator[](index).As<T>(); }
    template<class T> const ONLY_FOR_STRICT&  As(const size_t& index)const { return operator[](index).As<T>(); }
    template<class T> ONLY_FOR_STRICT&        As(const size_t& index)      { return operator[](index).As<T>(); }
    template<class T> COMPILE_ERROR_FOR_OTHER As(const size_t& index)const { static_assert( false, "THIS_TYPE_IS_NOT_SUPPORT" ); }
    template<class T> ONLY_FOR_MESSAGE        As(const size_t& index)const   
    {
        Message r;
        const auto& rec = As<Records>(index);
        r.mRecords = rec;
        return r;
    }
    template<class T> ONLY_FOR_MESSAGE        As(const Str& name)const   
    {
        Message r(name);
        const auto& rec = As<Records>(name);
        r.mRecords = rec;
        return r;
    }
public:
    void Clear()       { mRecords.clear();          }
    bool Empty()const  { return mRecords.size()==0; }
    size_t Size()const { return mRecords.size();    }
private:
    Records mRecords;
    Str     mName;
};

}//namespace tools


#if 0


#pragma once

#include "tools/value.h"

#define ONLY_FOR_INTEGER \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_INT, T >::type
#define ONLY_FOR_FLOATING \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_FLOAT, T >::type
#define ONLY_FOR_STRICT \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_STRICT, T >::type
#define ONLY_FOR_MESSAGE \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_MESSAGE, T >::type
#define COMPILE_ERROR_FOR_OTHER \
    typename ::std::enable_if< message_detail::PropertyType<T>::eIS_OTHER, T >::type

namespace tools
{
    struct Message;

    namespace message_detail
    {
        template<class T> struct PropertyType
        {
            enum { eIS_MESSAGE = ::std::is_same<T, Message>::value    };
            typedef typename ::std::conditional<eIS_MESSAGE, value_detail::V::Blob, T>::type Type;
            
            enum { eIS_INTEGER = ::std::numeric_limits<T>::is_integer };
            enum { eIS_FLOAT   = ::std::is_floating_point<T>::value   };
            enum { eIS_BOOL    = ::std::is_same<T,bool>::value        };
            enum { eIS_INT     = eIS_INTEGER && !eIS_BOOL             };
            enum { eIS_STRICT  = !eIS_FLOAT && !eIS_INT && !eIS_MESSAGE && value_detail::Type2Enum<Type>::eTYPE!=value_detail::V::eNULL };
            enum { eIS_OTHER   = !eIS_FLOAT && !eIS_INT && !eIS_STRICT && !eIS_MESSAGE };
        };
    }//namespace message_detail


    struct Message: value_detail::ValueTypes
    {
       ~Message(){}
        Message(const Str& name="",const size_t reserve=4);

        void SetName(const Str& name){ mName = name; }
        const Str& GetName()const { return mName;    }

        size_t Size()const;

        bool operator==(const Message& rigth)const;

        Message& operator()(const Str& name, const char* v);
        Message& operator()(const char* v);

        Message& operator()(const Str& name, const Message& v);
        Message& operator()(const Message& v);

        template<class T>Message& operator()(const Str& name, const T& v){ mRecords.emplace_back( name, Value::MakeMinsize(v) ); return *this; }
        template<class T>Message& operator()(const T& v){ return operator()("", v); }

        Message& Add(const Str& name, const char* v)               { return operator()(name, v); }
        Message& Add(const char* v)                                { return operator()(v);       }
        Message& Add(const Str& name, const Message& v)            { return operator()(name, v); }
        Message& Add(const Message& v)                             { return operator()(v);       }
        template<class T>Message& Add(const Str& name, const T& v) { return operator()(name, v); }
        template<class T>Message& Add(const T& v)                  { return operator()(v);       }
    public:
        template<class T> ONLY_FOR_INTEGER        As(const Str& name)const { return operator[](name).As<T>(); }
        template<class T> ONLY_FOR_FLOATING       As(const Str& name)const { return operator[](name).As<T>(); }
        template<class T> const ONLY_FOR_STRICT&  As(const Str& name)const { return operator[](name).As<T>(); }
        template<class T> ONLY_FOR_STRICT&        As(const Str& name)      { return operator[](name).As<T>(); }
        template<class T> COMPILE_ERROR_FOR_OTHER As(const Str& name)const { static_assert( false, "THIS_TYPE_IS_NOT_SUPPORT" ); }

        template<class T> ONLY_FOR_INTEGER        As(const size_t& index)const { return operator[](index).As<T>(); }
        template<class T> ONLY_FOR_FLOATING       As(const size_t& index)const { return operator[](index).As<T>(); }
        template<class T> const ONLY_FOR_STRICT&  As(const size_t& index)const { return operator[](index).As<T>(); }

        template<class T> ONLY_FOR_STRICT&        As(const size_t& index)      { return operator[](index).As<T>(); }
        template<class T> COMPILE_ERROR_FOR_OTHER As(const size_t& index)const { static_assert( false, "THIS_TYPE_IS_NOT_SUPPORT" ); }

        template<class T> ONLY_FOR_MESSAGE        As(const size_t& index)const   
        {
            Message r;
            const auto& rec = As<Records>(index);
            r.mRecords = rec;
            return r;
        }
        template<class T> ONLY_FOR_MESSAGE        As(const Str& name)const   
        {
            Message r(name);
            const auto& rec = As<Records>(name);
            r.mRecords = rec;
            return r;
        }

        
//         template<>Message As<Message>(const size_t& index)const 
//         {
//             Message r( Key(index) );
//             const auto& rec = As<Records>(index);
//             r.mRecords = rec;
//             return r;
//         }

        #undef ONLY_FOR_INTEGER
        #undef ONLY_FOR_FLOATING
        #undef ONLY_FOR_STRICT
        #undef COMPILE_ERROR_FOR_OTHER

    public:

        bool AttachedMessage(const size_t n)const;
        bool AttachedMessage(const Str& n)const;

        const Str& Key(const size_t n)const;

    public:
        template<class T>friend ::std::basic_ostream<T>& operator<<(::std::basic_ostream<T>& os, const Message& obj )
        {
            os<< "message name: "<<obj.mName<<endl;
            const Str d(2, ' ');
            for(const auto& r: obj.mRecords)
            {
                if(r.second.GetType()==eRECORDS)
                    ViewContent(os, r.first, r.second,1);
                else
                    os << d << r.first<<" : "<< r.second<< " as "<<r.second.GetTypeName()<< ::std::endl;
            }
            return os;
        }
    public:
        void Clear();
        void Clear(const size_t n);
        void Clear(const Str& n);
    
        bool Empty()const;
        bool Empty(const size_t n)const;
        bool Empty(const Str& n)const;
    private:
        const Value& operator[](const Str& name)const;
        Value& operator[](const Str& name);
        const Value& operator[](const size_t n)const;
        Value& operator[](const size_t n);

        void CheckIndex(const size_t index)const;

        template<class T> static void ViewContent(::std::basic_ostream<T>& os, const Str& key,  const Value& obj, const size_t deep=0)
        {
            const Str d( (deep+1)*2, ' ');
            os<< Str( deep*2, ' ') << "message name: "<< key <<endl;

            if(obj.GetType()!=eRECORDS)
                return cout << d << obj, void();
            
            const Records& recs = obj;
            for(const auto& r: recs)
            {
                if(r.second.GetType()==eRECORDS)
                    ViewContent(os, r.first, r.second, deep+1);
                else
                    os << d << r.first<<" : "<< r.second<< " as "<<r.second.GetTypeName()<< ::std::endl;
            }
        }


        Records mRecords;
        Str     mName;
    };

}//namespace tools


#endif